package com.zt.proxy.handler;

import java.lang.reflect.Field;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.codehaus.plexus.util.StringUtils;
import org.springframework.util.CollectionUtils;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry;
import com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient;
import com.alibaba.dubbo.remoting.zookeeper.curator.CuratorZookeeperTransporter;
import com.zt.proxy.cache.UrlCaches;
import com.zt.proxy.dto.ConnectDTO;
import com.zt.proxy.exception.ServiceException;
import com.zt.proxy.model.ApplicationModel;
import com.zt.proxy.model.ServiceModel;
import com.zt.proxy.model.UrlModel;
import com.zt.proxy.util.StringUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ZkClientHandler {

    private final String host;
    private final int port;
    private ZookeeperClient zkClient;
    private ZookeeperRegistry registry;
    private String root = "/dubbo";
    
    public ZkClientHandler(String host, int port) {
        super();
        this.host = host;
        this.port = port;
    }
    
    public void doConnect() throws NoSuchFieldException, IllegalAccessException {

       CuratorZookeeperTransporter zookeeperTransporter = new CuratorZookeeperTransporter();
       URL url = new URL("zookeeper", host, port);

       registry = new ZookeeperRegistry(url, zookeeperTransporter);

       Field field = registry.getClass().getDeclaredField("zkClient");
       field.setAccessible(true);
       zkClient = (ZookeeperClient) field.get(registry);
    }
    
    public List<ApplicationModel> getInterfaces(List<String> filterServiceNames) {
        List<ApplicationModel> ret = new ArrayList<>();
        List<String> services = zkClient.getChildren(root);
        services.forEach(service -> {
            List<String> urls = zkClient.getChildren("/dubbo/" + service + "/providers");
            urls.forEach(url -> {
                try {
                    url = URLDecoder.decode(url, "UTF-8");
                } catch (Exception e) {
                   log.error("URLDecode occur an Exception :{}", e);
                }
                log.info("============:{}", url);
                if (url.startsWith("dubbo://")) {
                    Map<String, String> map = StringUtil.parseKeyValuePair(url, "&");
                    String application = map.get("application");
                    if (StringUtils.isNotBlank(application)) {
                        ApplicationModel applicationModel = null ;
                        Optional<ApplicationModel> op = ret.stream().filter(model -> application.equals(model.getApplicationName())).findFirst();
                        if(!op.isPresent()){
                            applicationModel = ApplicationModel.builder().applicationName(application).services(new ArrayList<ServiceModel>())
                                    .build();
                            ret.add(applicationModel);
                        } else {
                            applicationModel = op.get();
                        }
                        if(CollectionUtils.isEmpty(filterServiceNames) || !filterServiceNames.contains(map.get("interface"))){
                            ServiceModel serviceModel = ServiceModel.builder().serviceName(map.get("interface")).build();
                            applicationModel.getServices().add(serviceModel);
                        }
                    }
                }
            });
        });
        log.info("load dubbo Interfaces success.");
        return ret;
    }

    public List<UrlModel> getProviders(ConnectDTO dto) {

        if (null == dto) {
            throw new ServiceException("dto can't be null.");
        }
        if (StringUtils.isEmpty(dto.getServiceName())) {
            throw new ServiceException("service name can't be null.");
        }

        Map<String, String> map = new HashMap<>();
        map.put(Constants.INTERFACE_KEY, dto.getServiceName());

        if (StringUtils.isNotEmpty(dto.getVersion())) {
            map.put(Constants.VERSION_KEY, dto.getVersion());
        }
        if (StringUtils.isNotEmpty(dto.getGroup())) {
            map.put(Constants.GROUP_KEY, dto.getGroup());
        }

        URL url = new URL("zookeeper", host, port, map);
        List<URL> list = registry.lookup(url);

        return UrlCaches.cache(dto.getServiceName(), list);
    }
    
    public void close() {
        registry.destroy();
    }

    public boolean isAvailable() {
        return registry.isAvailable();
    }
}
